1 Home Range Analysis

The data presented here was sampled from movebank.org and was originally published here:

Carrasco-Harris MF, Bowman D, Reichling S, Cole JA. 2020. Spatial ecology of copperhead snakes (Agkistrodon contortrix) in response to urban park trails. J Urban Ecol. 6(1):juaa007. https://doi.org/10.1093/jue/juaa007

In their study, the authors measured the spatial activity of copperheads (Agkistrodon contortrix) around pedestrian trails in an urban forest (Overton Park, Memphis, TN) using radio telemetry. They found that sex and season, not distance to the nearest trail, affected the distance that snakes moved and concluded that copperheads may be tolerant of low-level human disturbances.


2 Bring in data

Import Dataset with read.csv

snake.data <- read.csv("data/snakes.csv")
snake <- snake.data %>%
    filter(individual.local.identifier == c("ID_4241","ID_4270","ID_7270","ID_4266"))
longer object length is not a multiple of shorter object length


I decided to subset for just 4 individuals that I thought gave a good representation of the data. These snakes each primarily resided in different sections of the park and displayed variation in the sizes of their respective home ranges.


head(snake)


2.1 Looking for Outliers


The points appear to be have a similar spread for each individual without any clear outliers among them (at least to my eyes).


qaqc_plot <- ggplot() + geom_point(data=snake, 
                                   aes(utm.easting,utm.northing,
                                       color=individual.local.identifier)) +
                        labs(x="Easting", y="Northing") +
                        guides(color=guide_legend("Identifier"))

ggplotly(qaqc_plot)
lapply(split(snake, snake$individual.local.identifier), 
       function(x)write.csv(x, file = paste(x$individual.local.identifier[1],".csv", sep = ""), row.names = FALSE))
$ID_4241
NULL

$ID_4266
NULL

$ID_4270
NULL

$ID_7270
NULL
files <- list.files(path = ".", pattern = "[ID_]+[3413-7274]+", full.names = TRUE)


3 Analysis

utm_points <- cbind(snake$utm.easting, snake$utm.northing)
utm_locations <- SpatialPoints(utm_points, 
                 proj4string=CRS("+proj=utm +zone=16 +datum=WGS84"))
proj_lat.lon <- as.data.frame(spTransform(
                utm_locations, CRS("+proj=longlat +datum=WGS84")))
colnames(proj_lat.lon) <- c("x","y")
raster2 <- openmap(c(max(proj_lat.lon$y)+0.003, min(proj_lat.lon$x)-0.003), 
                  c(min(proj_lat.lon$y)-0.003, max(proj_lat.lon$x)+0.003), 
                  type = "bing")
raster <- openmap(c(max(proj_lat.lon$y)+0.01, min(proj_lat.lon$x)-0.01), 
                  c(min(proj_lat.lon$y)-0.01, max(proj_lat.lon$x)+0.01), 
                  type = "bing")
raster_utm <- openproj(raster, 
              projection = "+proj=utm +zone=16 +datum=WGS84 +units=m +no_defs")
raster2_utm <- openproj(raster2, 
              projection = "+proj=utm +zone=16 +datum=WGS84 +units=m +no_defs")


I decided to make a second raster that still takes into account the location of the points when choosing the maximum and minimum coordinates of the resulting map, but with a smaller window to get us slightly more zoom over our park (which represents such a small area).

Here’s the map made with the raster inputs from Dr. Gentry’s code:


autoplot.OpenStreetMap(raster_utm, expand = TRUE) + theme_bw() +
  theme(legend.position="right") +
  theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
  geom_point(data=snake, aes(utm.easting,utm.northing,
             color=individual.local.identifier), size = 1, alpha = 0.8) +
  theme(axis.title = element_text(face="bold")) + labs(x="Easting",
        y="Northing") + guides(color=guide_legend("Identifier"))


And with a constrained window:


autoplot.OpenStreetMap(raster2_utm, expand = TRUE) + theme_bw() +
  theme(legend.position="right") +
  theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
  geom_point(data=snake, aes(utm.easting,utm.northing,
             color=individual.local.identifier), size = 1, alpha = 0.8) +
  theme(axis.title = element_text(face="bold")) + labs(x="Easting",
        y="Northing") + guides(color=guide_legend("Identifier"))


4 Home Range Analysis


4.1 Minimum Convex Polygon


For some reason I was having an issue with the permit status (?) using the pbapply package. For whatever reason and completely mysteriously to me, the first three lines of code in this chunk fixed the issue for me


library(maptools)
if (!require(gpclib)) install.packages("gpclib", type="source")
gpclibPermit() #This resolved the following error message from pbapply: "Error: isTRUE(gpclibPermitStatus()) is not TRUE"
[1] TRUE
mcp_raster <- function(filename){
  data <- read.csv(file = filename)
  x <- as.data.frame(data$utm.easting)
  y <- as.data.frame(data$utm.northing)
  xy <- c(x,y)
  data.proj <- SpatialPointsDataFrame(xy,data, proj4string = CRS("+proj=utm +zone=16 +datum=WGS84 +units=m +no_defs"))
  xy <- SpatialPoints(data.proj@coords)
  mcp.out <- mcp(xy, percent=100, unout="ha")
  mcp.points <- cbind((data.frame(xy)),data$individual.local.identifier)
  colnames(mcp.points) <- c("x","y", "identifier")
  mcp.poly <- fortify(mcp.out, region = "id")
  units <- grid.text(paste(round(mcp.out@data$area,2),"ha"), x=0.85,  y=0.95,
                     gp=gpar(fontface=4, col="white", cex=0.9), draw = FALSE)
  mcp.plot <- autoplot.OpenStreetMap(raster2_utm, expand = TRUE) + theme_bw() + theme(legend.position="none") +
    theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
    geom_polygon(data=mcp.poly, aes(x=mcp.poly$long, y=mcp.poly$lat), alpha=0.5, fill = "red") +
    geom_point(data=mcp.points, aes(x=x, y=y)) + 
    labs(x="Easting (m)", y="Northing (m)", title=mcp.points$identifier) +
    theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5)) + 
    annotation_custom(units)
  mcp.plot
}

pblapply(files, mcp_raster)

  |                                                             | 0 % ~calculating  
  |================                                             | 25% ~01s          
  |===============================                              | 50% ~00s          
  |==============================================               | 75% ~00s          
  |=============================================================| 100% elapsed=01s  
[[1]]

[[2]]

[[3]]

[[4]]


4.2 Kernel-Density Estimation

kde_raster <- function(filename){
  data <- read.csv(file = filename)
  x <- as.data.frame(data$utm.easting)
  y <- as.data.frame(data$utm.northing)
  xy <- c(x,y)
  data.proj <- SpatialPointsDataFrame(xy,data, proj4string = CRS("+proj=utm +zone=16 +datum=WGS84 +units=m +no_defs"))
  xy <- SpatialPoints(data.proj@coords)
  kde<-kernelUD(xy, h="href", kern="bivnorm", grid=100)
  ver <- getverticeshr(kde, 95)
  kde.points <- cbind((data.frame(data.proj@coords)),data$individual.local.identifier)
  colnames(kde.points) <- c("x","y","identifier")
  kde.poly <- fortify(ver, region = "id")
  units <- grid.text(paste(round(ver$area,2)," ha"), x=0.85,  y=0.95,
                     gp=gpar(fontface=4, col="white", cex=0.9), draw = FALSE)
  kde.plot <- autoplot.OpenStreetMap(raster2_utm, expand = TRUE) + theme_bw() + theme(legend.position="none") +
    theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
    geom_polygon(data=kde.poly, aes(x=kde.poly$long, y=kde.poly$lat), alpha = 0.5, fill = "red") +
    geom_point(data=kde.points, aes(x=x, y=y)) +
    labs(x="Easting (m)", y="Northing (m)", title=kde.points$identifier) +
    theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5)) + 
    annotation_custom(units)
  kde.plot
}

pblapply(files, kde_raster)

  |                                                             | 0 % ~calculating  
  |================                                             | 25% ~02s          
  |===============================                              | 50% ~01s          
  |==============================================               | 75% ~00s          
  |=============================================================| 100% elapsed=01s  
[[1]]

[[2]]

[[3]]

[[4]]


4.3 Brownian Bridge Movement

snake_4241 <- read.csv("ID_4241.csv")
date <- as.POSIXct(strptime(as.character(snake_4241$timestamp),"%Y-%m-%d %H:%M:%S", tz="Asia/Bangkok"))
snake_4241$date <- date
snake_4241.reloc <- cbind.data.frame(snake_4241$utm.easting, snake_4241$utm.northing,
                                as.vector(snake_4241$individual.local.identifier),
                                as.POSIXct(date))
colnames(snake_4241.reloc) <- c("x","y","id","date")
trajectory <- as.ltraj(snake_4241.reloc, date=date, id="snake_4241")
sig1 <- liker(trajectory, sig2 = 58, rangesig1 = c(0, 5), plotit = FALSE)
snake_4241.traj <- kernelbb(trajectory, sig1 = .7908, sig2 = 58, grid = 100)
bb_ver <- getverticeshr(snake_4241.traj, 58)
bb_poly <- fortify(bb_ver, region = "id", 
                   proj4string = CRS("+proj=utm +zone=16+
                                     datum=WGS84 +units=m +no_defs"))
colnames(bb_poly) <- c("x","y","order","hole","piece","id","group")
bb_image <- crop(snake_4241.traj, bb_ver, 
                 proj4string = CRS("+proj=utm +zone=16 +
                                   datum=WGS84 +units=m +no_defs"))
bb_units <- grid.text(paste(round(bb_ver$area,2)," ha"), x=0.85,  y=0.95,
                   gp=gpar(fontface=4, col="white", cex=0.9), draw = FALSE)
bb.plot <- autoplot.OpenStreetMap(raster2_utm, expand = TRUE) + theme_bw() + theme(legend.position="none") +
  theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
  geom_tile(data=bb_image, 
            aes(x=bb_image@coords[,1], y=bb_image@coords[,2],
            fill = bb_image@data$ud)) +
  geom_polygon(data=bb_poly, aes(x=x, y=y, group = group), color = "black", fill = NA) +
  scale_fill_viridis_c(option = "inferno") + annotation_custom(bb_units) +
  labs(x="Easting (m)", y="Northing (m)", title="4241") +
  theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5))
bb.plot


snake_4270 <- read.csv("ID_4270.csv")
date <- as.POSIXct(strptime(as.character(snake_4270$timestamp),"%Y-%m-%d %H:%M:%S", tz="Asia/Bangkok"))
snake_4270$date <- date
snake_4270.reloc <- cbind.data.frame(snake_4270$utm.easting, snake_4270$utm.northing,
                                as.vector(snake_4270$individual.local.identifier),
                                as.POSIXct(date))
colnames(snake_4270.reloc) <- c("x","y","id","date")
trajectory <- as.ltraj(snake_4270.reloc, date=date, id="snake_4270")
sig1 <- liker(trajectory, sig2 = 58, rangesig1 = c(0, 5), plotit = FALSE)
snake_4270.traj <- kernelbb(trajectory, sig1 = .7908, sig2 = 58, grid = 100)
bb_ver <- getverticeshr(snake_4270.traj, 24.5)
bb_poly <- fortify(bb_ver, region = "id", 
                   proj4string = CRS("+proj=utm +zone=16+
                                     datum=WGS84 +units=m +no_defs"))
colnames(bb_poly) <- c("x","y","order","hole","piece","id","group")
bb_image <- crop(snake_4270.traj, bb_ver, 
                 proj4string = CRS("+proj=utm +zone=16 +
                                   datum=WGS84 +units=m +no_defs"))
bb_units <- grid.text(paste(round(bb_ver$area,2)," ha"), x=0.85,  y=0.95,
                   gp=gpar(fontface=4, col="white", cex=0.9), draw = FALSE)
bb.plot <- autoplot.OpenStreetMap(raster2_utm, expand = TRUE) + theme_bw() + theme(legend.position="none") +
  theme(panel.border = element_rect(colour = "black", fill=NA, size=1)) +
  geom_tile(data=bb_image, 
            aes(x=bb_image@coords[,1], y=bb_image@coords[,2],
            fill = bb_image@data$ud)) +
  geom_polygon(data=bb_poly, aes(x=x, y=y, group = group), color = "black", fill = NA) +
  scale_fill_viridis_c(option = "inferno") + annotation_custom(bb_units) +
  labs(x="Easting (m)", y="Northing (m)", title="4270") +
  theme(legend.position="none", plot.title = element_text(face = "bold", hjust = 0.5))
bb.plot


Interestingly, even though 4270 had a home range that was fairly comparable to 4241 in size (at least by mcp), adding the timestamp data and generating this ‘heat map’ of activity shows that 4270’s activity was generally concentrated in a much denser area.

LS0tCnRpdGxlOiBIb21lIFJhbmdlIEFuYWx5c2lzIDxicj48c21hbGw+QWR2YW5jZWQgRGF0YSBBbmFseXRpY3M8L3NtYWxsPjwvYnI+CmF1dGhvcjogIkF1c3RpbiBQZWF5IFN0YXRlIFVuaXZlcnNpdHkiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICByb3dzLnByaW50OiAxMAogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IGJyZWV6ZWRhcmsKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCiAgICAgIHNtb290aF9zY3JvbGw6IHllcwogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IAogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICByb3dzLnByaW50OiAxMAogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IGJyZWV6ZWRhcmsKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCiAgICAgIHNtb290aF9zY3JvbGw6IHllcwplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCiAgbW9kZTogZ2ZtCi0tLQo8Ym9keSBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojRERBMTVFOyI+CmBgYHs9aHRtbH0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCmgxLnRpdGxlIHsKICBmb250LXNpemU6IDQwcHg7CiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgY29sb3I6IEJsYWNrOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICBmb250LXNpemU6IDI1cHg7CiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgY29sb3I6ICNEMDIzNDk7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9Cgpib2R5IHsKICBmb250LWZhbWlseTogSGVsdmV0aWNhOwogIGZvbnQtc2l6ZTogMTJwdDsKfQoKLnpvb20gewogIHRyYW5zZm9ybS1vcmlnaW46IDQwJSA1MCUgMDsKICB0cmFuc2l0aW9uOiB0cmFuc2Zvcm0gLjJzOwogIG1hcmdpbjogMCBhdXRvOwp9Ci56b29tIGltZ3sKCXdpZHRoOmF1dG87CgloZWlnaHQ6YXV0bzsJCn0KLnpvb206aG92ZXIgewogIHRyYW5zZm9ybTogc2NhbGUoMik7Cn0KCnRoLCB0ZCB7cGFkZGluZzogNXB4O30KCjwvc3R5bGU+CmBgYAoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwYWNrYWdlczwtYygiYWRlaGFiaXRhdEhSIiwiZGF0YS50YWJsZSIsImdnZm9ydGlmeSIsImdyaWQiLCJtb3ZlIiwibW92ZVZpcyIsIk9wZW5TdHJlZXRNYXAiLCJwYmFwcGx5IiwicGxvdGx5IiwicmdkYWwiLCJzcCIsInRpZHl2ZXJzZSIsInZpcmlkaXMiLCAicmdlb3MiLCJkcGx5ciIpCgpzYXBwbHkocGFja2FnZXMsIGxpYnJhcnksIGNoYXJhY3Rlci5vbmx5PVQpCmBgYAoKIyBIb21lIFJhbmdlIEFuYWx5c2lzIAoKVGhlIGRhdGEgcHJlc2VudGVkIGhlcmUgd2FzIHNhbXBsZWQgZnJvbSBtb3ZlYmFuay5vcmcgYW5kIHdhcyBvcmlnaW5hbGx5IHB1Ymxpc2hlZCBoZXJlOiAKCioqQ2FycmFzY28tSGFycmlzIE1GLCBCb3dtYW4gRCwgUmVpY2hsaW5nIFMsIENvbGUgSkEuIDIwMjAuIFNwYXRpYWwgZWNvbG9neSBvZiBjb3BwZXJoZWFkIHNuYWtlcyAoQWdraXN0cm9kb24gY29udG9ydHJpeCkgaW4gcmVzcG9uc2UgdG8gdXJiYW4gcGFyayB0cmFpbHMuIEogVXJiYW4gRWNvbC4gNigxKTpqdWFhMDA3LiBodHRwczovL2RvaS5vcmcvMTAuMTA5My9qdWUvanVhYTAwNyoqIAoKSW4gdGhlaXIgc3R1ZHksIHRoZSBhdXRob3JzIG1lYXN1cmVkIHRoZSBzcGF0aWFsIGFjdGl2aXR5IG9mIGNvcHBlcmhlYWRzICgqQWdraXN0cm9kb24gY29udG9ydHJpeCopIGFyb3VuZCBwZWRlc3RyaWFuIHRyYWlscyBpbiBhbiB1cmJhbiBmb3Jlc3QgKE92ZXJ0b24gUGFyaywgTWVtcGhpcywgVE4pIHVzaW5nIHJhZGlvIHRlbGVtZXRyeS4gVGhleSBmb3VuZCB0aGF0IHNleCBhbmQgc2Vhc29uLCBub3QgZGlzdGFuY2UgdG8gdGhlIG5lYXJlc3QgdHJhaWwsIGFmZmVjdGVkIHRoZSBkaXN0YW5jZSB0aGF0IHNuYWtlcyBtb3ZlZCBhbmQgY29uY2x1ZGVkIHRoYXQgY29wcGVyaGVhZHMgbWF5IGJlIHRvbGVyYW50IG9mIGxvdy1sZXZlbCBodW1hbiBkaXN0dXJiYW5jZXMuIAoKIVtdKGltYWdlcy9jb3BwZXJoZWFkLnBuZykKCjxicj4KCiMgQnJpbmcgaW4gZGF0YSAKCjxkZXRhaWxzPjxzdW1tYXJ5PjxiaWc+SW1wb3J0IERhdGFzZXQgd2l0aCBgYGByZWFkLmNzdmBgYDwvYmlnPjwvc3VtbWFyeT4KYGBge3IgcmVhZCBpbiBkYXRhfQpzbmFrZS5kYXRhIDwtIHJlYWQuY3N2KCJkYXRhL3NuYWtlcy5jc3YiKQpzbmFrZSA8LSBzbmFrZS5kYXRhICU+JQogICAgZmlsdGVyKGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllciA9PSBjKCJJRF80MjQxIiwiSURfNDI3MCIsIklEXzcyNzAiLCJJRF80MjY2IikpCmBgYAoKPGJyPiAKCioqSSBkZWNpZGVkIHRvIHN1YnNldCBmb3IganVzdCA0IGluZGl2aWR1YWxzIHRoYXQgSSB0aG91Z2h0IGdhdmUgYSBnb29kIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkYXRhLiBUaGVzZSBzbmFrZXMgZWFjaCBwcmltYXJpbHkgcmVzaWRlZCBpbiBkaWZmZXJlbnQgc2VjdGlvbnMgb2YgdGhlIHBhcmsgYW5kIGRpc3BsYXllZCB2YXJpYXRpb24gaW4gdGhlIHNpemVzIG9mIHRoZWlyIHJlc3BlY3RpdmUgaG9tZSByYW5nZXMuKiogCgo8YnI+Cgo8L2RldGFpbHM+CmBgYHtyfQpoZWFkKHNuYWtlKQpgYGAKCjxicj4KCiMjIExvb2tpbmcgZm9yIE91dGxpZXJzIAoKPGJyPgoKKipUaGUgcG9pbnRzIGFwcGVhciB0byBiZSBoYXZlIGEgc2ltaWxhciBzcHJlYWQgZm9yIGVhY2ggaW5kaXZpZHVhbCB3aXRob3V0IGFueSBjbGVhciBvdXRsaWVycyBhbW9uZyB0aGVtIChhdCBsZWFzdCB0byBteSBleWVzKS4qKgoKPGJyPgoKYGBge3IgcWFxYyBwbG90LCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnFhcWNfcGxvdCA8LSBnZ3Bsb3QoKSArIGdlb21fcG9pbnQoZGF0YT1zbmFrZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHV0bS5lYXN0aW5nLHV0bS5ub3J0aGluZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9aW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyKSkgKwogICAgICAgICAgICAgICAgICAgICAgICBsYWJzKHg9IkVhc3RpbmciLCB5PSJOb3J0aGluZyIpICsKICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGVzKGNvbG9yPWd1aWRlX2xlZ2VuZCgiSWRlbnRpZmllciIpKQoKZ2dwbG90bHkocWFxY19wbG90KQpgYGAKCmBgYHtyIHNwbGl0IGRhdGEsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGFwcGx5KHNwbGl0KHNuYWtlLCBzbmFrZSRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpLCAKICAgICAgIGZ1bmN0aW9uKHgpd3JpdGUuY3N2KHgsIGZpbGUgPSBwYXN0ZSh4JGluZGl2aWR1YWwubG9jYWwuaWRlbnRpZmllclsxXSwiLmNzdiIsIHNlcCA9ICIiKSwgcm93Lm5hbWVzID0gRkFMU0UpKQpgYGAKCmBgYHtyIG1ha2UgbGlzdCBvZiBpbmRpdmlkdWFsIGZpbGVzLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZpbGVzIDwtIGxpc3QuZmlsZXMocGF0aCA9ICIuIiwgcGF0dGVybiA9ICJbSURfXStbMzQxMy03Mjc0XSsiLCBmdWxsLm5hbWVzID0gVFJVRSkKYGBgCgo8YnI+CgojIEFuYWx5c2lzIAoKYGBge3IgZmlyc3QgYW5hbHlzaXMsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KdXRtX3BvaW50cyA8LSBjYmluZChzbmFrZSR1dG0uZWFzdGluZywgc25ha2UkdXRtLm5vcnRoaW5nKQp1dG1fbG9jYXRpb25zIDwtIFNwYXRpYWxQb2ludHModXRtX3BvaW50cywgCiAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmc9Q1JTKCIrcHJvaj11dG0gK3pvbmU9MTYgK2RhdHVtPVdHUzg0IikpCnByb2pfbGF0LmxvbiA8LSBhcy5kYXRhLmZyYW1lKHNwVHJhbnNmb3JtKAogICAgICAgICAgICAgICAgdXRtX2xvY2F0aW9ucywgQ1JTKCIrcHJvaj1sb25nbGF0ICtkYXR1bT1XR1M4NCIpKSkKY29sbmFtZXMocHJval9sYXQubG9uKSA8LSBjKCJ4IiwieSIpCnJhc3RlcjIgPC0gb3Blbm1hcChjKG1heChwcm9qX2xhdC5sb24keSkrMC4wMDMsIG1pbihwcm9qX2xhdC5sb24keCktMC4wMDMpLCAKICAgICAgICAgICAgICAgICAgYyhtaW4ocHJval9sYXQubG9uJHkpLTAuMDAzLCBtYXgocHJval9sYXQubG9uJHgpKzAuMDAzKSwgCiAgICAgICAgICAgICAgICAgIHR5cGUgPSAiYmluZyIpCnJhc3RlciA8LSBvcGVubWFwKGMobWF4KHByb2pfbGF0LmxvbiR5KSswLjAxLCBtaW4ocHJval9sYXQubG9uJHgpLTAuMDEpLCAKICAgICAgICAgICAgICAgICAgYyhtaW4ocHJval9sYXQubG9uJHkpLTAuMDEsIG1heChwcm9qX2xhdC5sb24keCkrMC4wMSksIAogICAgICAgICAgICAgICAgICB0eXBlID0gImJpbmciKQpyYXN0ZXJfdXRtIDwtIG9wZW5wcm9qKHJhc3RlciwgCiAgICAgICAgICAgICAgcHJvamVjdGlvbiA9ICIrcHJvaj11dG0gK3pvbmU9MTYgK2RhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikKcmFzdGVyMl91dG0gPC0gb3BlbnByb2oocmFzdGVyMiwgCiAgICAgICAgICAgICAgcHJvamVjdGlvbiA9ICIrcHJvaj11dG0gK3pvbmU9MTYgK2RhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikKYGBgCgo8YnI+IAoKKipJIGRlY2lkZWQgdG8gbWFrZSBhIHNlY29uZCByYXN0ZXIgdGhhdCBzdGlsbCB0YWtlcyBpbnRvIGFjY291bnQgdGhlIGxvY2F0aW9uIG9mIHRoZSBwb2ludHMgd2hlbiBjaG9vc2luZyB0aGUgbWF4aW11bSBhbmQgbWluaW11bSBjb29yZGluYXRlcyBvZiB0aGUgcmVzdWx0aW5nIG1hcCwgYnV0IHdpdGggYSBzbWFsbGVyIHdpbmRvdyB0byBnZXQgdXMgc2xpZ2h0bHkgbW9yZSB6b29tIG92ZXIgb3VyIHBhcmsgKHdoaWNoIHJlcHJlc2VudHMgc3VjaCBhIHNtYWxsIGFyZWEpLioqIAoKKipIZXJlJ3MgdGhlIG1hcCBtYWRlIHdpdGggdGhlIHJhc3RlciBpbnB1dHMgZnJvbSBEci4gR2VudHJ5J3MgY29kZToqKgoKPGJyPgoKYGBge3IgcmFzdGVyIG1hcCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYXV0b3Bsb3QuT3BlblN0cmVldE1hcChyYXN0ZXJfdXRtLCBleHBhbmQgPSBUUlVFKSArIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemU9MSkpICsKICBnZW9tX3BvaW50KGRhdGE9c25ha2UsIGFlcyh1dG0uZWFzdGluZyx1dG0ubm9ydGhpbmcsCiAgICAgICAgICAgICBjb2xvcj1pbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpLCBzaXplID0gMSwgYWxwaGEgPSAwLjgpICsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKSkgKyBsYWJzKHg9IkVhc3RpbmciLAogICAgICAgIHk9Ik5vcnRoaW5nIikgKyBndWlkZXMoY29sb3I9Z3VpZGVfbGVnZW5kKCJJZGVudGlmaWVyIikpCmBgYAoKPGJyPiAKCioqQW5kIHdpdGggYSBjb25zdHJhaW5lZCB3aW5kb3c6KiogCgo8YnI+CgpgYGB7ciByYXN0ZXIyIG1hcCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYXV0b3Bsb3QuT3BlblN0cmVldE1hcChyYXN0ZXIyX3V0bSwgZXhwYW5kID0gVFJVRSkgKyB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BLCBzaXplPTEpKSArCiAgZ2VvbV9wb2ludChkYXRhPXNuYWtlLCBhZXModXRtLmVhc3RpbmcsdXRtLm5vcnRoaW5nLAogICAgICAgICAgICAgY29sb3I9aW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyKSwgc2l6ZSA9IDEsIGFscGhhID0gMC44KSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpICsgbGFicyh4PSJFYXN0aW5nIiwKICAgICAgICB5PSJOb3J0aGluZyIpICsgZ3VpZGVzKGNvbG9yPWd1aWRlX2xlZ2VuZCgiSWRlbnRpZmllciIpKQpgYGAKCjxicj4KCiMgSG9tZSBSYW5nZSBBbmFseXNpcyAKCjxicj4KCiMjIE1pbmltdW0gQ29udmV4IFBvbHlnb24gCgo8YnI+IAoKKipGb3Igc29tZSByZWFzb24gSSB3YXMgaGF2aW5nIGFuIGlzc3VlIHdpdGggdGhlIHBlcm1pdCBzdGF0dXMgKD8pIHVzaW5nIHRoZSBwYmFwcGx5IHBhY2thZ2UuIEZvciB3aGF0ZXZlciByZWFzb24gYW5kIGNvbXBsZXRlbHkgbXlzdGVyaW91c2x5IHRvIG1lLCB0aGUgZmlyc3QgdGhyZWUgbGluZXMgb2YgY29kZSBpbiB0aGlzIGNodW5rIGZpeGVkIHRoZSBpc3N1ZSBmb3IgbWUqKiAKCjxicj4KCmBgYHtyIG1pbmltdW0gY29udmV4IHBvbHlnb25zLGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KG1hcHRvb2xzKQppZiAoIXJlcXVpcmUoZ3BjbGliKSkgaW5zdGFsbC5wYWNrYWdlcygiZ3BjbGliIiwgdHlwZT0ic291cmNlIikKZ3BjbGliUGVybWl0KCkgI1RoaXMgcmVzb2x2ZWQgdGhlIGZvbGxvd2luZyBlcnJvciBtZXNzYWdlIGZyb20gcGJhcHBseTogIkVycm9yOiBpc1RSVUUoZ3BjbGliUGVybWl0U3RhdHVzKCkpIGlzIG5vdCBUUlVFIgoKbWNwX3Jhc3RlciA8LSBmdW5jdGlvbihmaWxlbmFtZSl7CiAgZGF0YSA8LSByZWFkLmNzdihmaWxlID0gZmlsZW5hbWUpCiAgeCA8LSBhcy5kYXRhLmZyYW1lKGRhdGEkdXRtLmVhc3RpbmcpCiAgeSA8LSBhcy5kYXRhLmZyYW1lKGRhdGEkdXRtLm5vcnRoaW5nKQogIHh5IDwtIGMoeCx5KQogIGRhdGEucHJvaiA8LSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKHh5LGRhdGEsIHByb2o0c3RyaW5nID0gQ1JTKCIrcHJvaj11dG0gK3pvbmU9MTYgK2RhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikpCiAgeHkgPC0gU3BhdGlhbFBvaW50cyhkYXRhLnByb2pAY29vcmRzKQogIG1jcC5vdXQgPC0gbWNwKHh5LCBwZXJjZW50PTEwMCwgdW5vdXQ9ImhhIikKICBtY3AucG9pbnRzIDwtIGNiaW5kKChkYXRhLmZyYW1lKHh5KSksZGF0YSRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpCiAgY29sbmFtZXMobWNwLnBvaW50cykgPC0gYygieCIsInkiLCAiaWRlbnRpZmllciIpCiAgbWNwLnBvbHkgPC0gZm9ydGlmeShtY3Aub3V0LCByZWdpb24gPSAiaWQiKQogIHVuaXRzIDwtIGdyaWQudGV4dChwYXN0ZShyb3VuZChtY3Aub3V0QGRhdGEkYXJlYSwyKSwiaGEiKSwgeD0wLjg1LCAgeT0wLjk1LAogICAgICAgICAgICAgICAgICAgICBncD1ncGFyKGZvbnRmYWNlPTQsIGNvbD0id2hpdGUiLCBjZXg9MC45KSwgZHJhdyA9IEZBTFNFKQogIG1jcC5wbG90IDwtIGF1dG9wbG90Lk9wZW5TdHJlZXRNYXAocmFzdGVyMl91dG0sIGV4cGFuZCA9IFRSVUUpICsgdGhlbWVfYncoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICAgIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BLCBzaXplPTEpKSArCiAgICBnZW9tX3BvbHlnb24oZGF0YT1tY3AucG9seSwgYWVzKHg9bWNwLnBvbHkkbG9uZywgeT1tY3AucG9seSRsYXQpLCBhbHBoYT0wLjUsIGZpbGwgPSAicmVkIikgKwogICAgZ2VvbV9wb2ludChkYXRhPW1jcC5wb2ludHMsIGFlcyh4PXgsIHk9eSkpICsgCiAgICBsYWJzKHg9IkVhc3RpbmcgKG0pIiwgeT0iTm9ydGhpbmcgKG0pIiwgdGl0bGU9bWNwLnBvaW50cyRpZGVudGlmaWVyKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSkgKyAKICAgIGFubm90YXRpb25fY3VzdG9tKHVuaXRzKQogIG1jcC5wbG90Cn0KCnBibGFwcGx5KGZpbGVzLCBtY3BfcmFzdGVyKQpgYGAKCjxicj4KCiMjIEtlcm5lbC1EZW5zaXR5IEVzdGltYXRpb24gCgpgYGB7ciBrZXJuZWwgZGVuc2l0eSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmtkZV9yYXN0ZXIgPC0gZnVuY3Rpb24oZmlsZW5hbWUpewogIGRhdGEgPC0gcmVhZC5jc3YoZmlsZSA9IGZpbGVuYW1lKQogIHggPC0gYXMuZGF0YS5mcmFtZShkYXRhJHV0bS5lYXN0aW5nKQogIHkgPC0gYXMuZGF0YS5mcmFtZShkYXRhJHV0bS5ub3J0aGluZykKICB4eSA8LSBjKHgseSkKICBkYXRhLnByb2ogPC0gU3BhdGlhbFBvaW50c0RhdGFGcmFtZSh4eSxkYXRhLCBwcm9qNHN0cmluZyA9IENSUygiK3Byb2o9dXRtICt6b25lPTE2ICtkYXR1bT1XR1M4NCArdW5pdHM9bSArbm9fZGVmcyIpKQogIHh5IDwtIFNwYXRpYWxQb2ludHMoZGF0YS5wcm9qQGNvb3JkcykKICBrZGU8LWtlcm5lbFVEKHh5LCBoPSJocmVmIiwga2Vybj0iYml2bm9ybSIsIGdyaWQ9MTAwKQogIHZlciA8LSBnZXR2ZXJ0aWNlc2hyKGtkZSwgOTUpCiAga2RlLnBvaW50cyA8LSBjYmluZCgoZGF0YS5mcmFtZShkYXRhLnByb2pAY29vcmRzKSksZGF0YSRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpCiAgY29sbmFtZXMoa2RlLnBvaW50cykgPC0gYygieCIsInkiLCJpZGVudGlmaWVyIikKICBrZGUucG9seSA8LSBmb3J0aWZ5KHZlciwgcmVnaW9uID0gImlkIikKICB1bml0cyA8LSBncmlkLnRleHQocGFzdGUocm91bmQodmVyJGFyZWEsMiksIiBoYSIpLCB4PTAuODUsICB5PTAuOTUsCiAgICAgICAgICAgICAgICAgICAgIGdwPWdwYXIoZm9udGZhY2U9NCwgY29sPSJ3aGl0ZSIsIGNleD0wLjkpLCBkcmF3ID0gRkFMU0UpCiAga2RlLnBsb3QgPC0gYXV0b3Bsb3QuT3BlblN0cmVldE1hcChyYXN0ZXIyX3V0bSwgZXhwYW5kID0gVFJVRSkgKyB0aGVtZV9idygpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemU9MSkpICsKICAgIGdlb21fcG9seWdvbihkYXRhPWtkZS5wb2x5LCBhZXMoeD1rZGUucG9seSRsb25nLCB5PWtkZS5wb2x5JGxhdCksIGFscGhhID0gMC41LCBmaWxsID0gInJlZCIpICsKICAgIGdlb21fcG9pbnQoZGF0YT1rZGUucG9pbnRzLCBhZXMoeD14LCB5PXkpKSArCiAgICBsYWJzKHg9IkVhc3RpbmcgKG0pIiwgeT0iTm9ydGhpbmcgKG0pIiwgdGl0bGU9a2RlLnBvaW50cyRpZGVudGlmaWVyKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSkgKyAKICAgIGFubm90YXRpb25fY3VzdG9tKHVuaXRzKQogIGtkZS5wbG90Cn0KCnBibGFwcGx5KGZpbGVzLCBrZGVfcmFzdGVyKQpgYGAKCjxicj4KCiMjIEJyb3duaWFuIEJyaWRnZSBNb3ZlbWVudAoKYGBge3IgQnJvd25pYW4gQnJpZGdlIE1vdmVtZW50LCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0Kc25ha2VfNDI0MSA8LSByZWFkLmNzdigiSURfNDI0MS5jc3YiKQpkYXRlIDwtIGFzLlBPU0lYY3Qoc3RycHRpbWUoYXMuY2hhcmFjdGVyKHNuYWtlXzQyNDEkdGltZXN0YW1wKSwiJVktJW0tJWQgJUg6JU06JVMiLCB0ej0iQXNpYS9CYW5na29rIikpCnNuYWtlXzQyNDEkZGF0ZSA8LSBkYXRlCnNuYWtlXzQyNDEucmVsb2MgPC0gY2JpbmQuZGF0YS5mcmFtZShzbmFrZV80MjQxJHV0bS5lYXN0aW5nLCBzbmFrZV80MjQxJHV0bS5ub3J0aGluZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy52ZWN0b3Ioc25ha2VfNDI0MSRpbmRpdmlkdWFsLmxvY2FsLmlkZW50aWZpZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLlBPU0lYY3QoZGF0ZSkpCmNvbG5hbWVzKHNuYWtlXzQyNDEucmVsb2MpIDwtIGMoIngiLCJ5IiwiaWQiLCJkYXRlIikKdHJhamVjdG9yeSA8LSBhcy5sdHJhaihzbmFrZV80MjQxLnJlbG9jLCBkYXRlPWRhdGUsIGlkPSJzbmFrZV80MjQxIikKc2lnMSA8LSBsaWtlcih0cmFqZWN0b3J5LCBzaWcyID0gNTgsIHJhbmdlc2lnMSA9IGMoMCwgNSksIHBsb3RpdCA9IEZBTFNFKQpzbmFrZV80MjQxLnRyYWogPC0ga2VybmVsYmIodHJhamVjdG9yeSwgc2lnMSA9IC43OTA4LCBzaWcyID0gNTgsIGdyaWQgPSAxMDApCmJiX3ZlciA8LSBnZXR2ZXJ0aWNlc2hyKHNuYWtlXzQyNDEudHJhaiwgNTgpCmJiX3BvbHkgPC0gZm9ydGlmeShiYl92ZXIsIHJlZ2lvbiA9ICJpZCIsIAogICAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmcgPSBDUlMoIitwcm9qPXV0bSArem9uZT0xNisKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikpCmNvbG5hbWVzKGJiX3BvbHkpIDwtIGMoIngiLCJ5Iiwib3JkZXIiLCJob2xlIiwicGllY2UiLCJpZCIsImdyb3VwIikKYmJfaW1hZ2UgPC0gY3JvcChzbmFrZV80MjQxLnRyYWosIGJiX3ZlciwgCiAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmcgPSBDUlMoIitwcm9qPXV0bSArem9uZT0xNiArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKSkKYmJfdW5pdHMgPC0gZ3JpZC50ZXh0KHBhc3RlKHJvdW5kKGJiX3ZlciRhcmVhLDIpLCIgaGEiKSwgeD0wLjg1LCAgeT0wLjk1LAogICAgICAgICAgICAgICAgICAgZ3A9Z3Bhcihmb250ZmFjZT00LCBjb2w9IndoaXRlIiwgY2V4PTAuOSksIGRyYXcgPSBGQUxTRSkKYmIucGxvdCA8LSBhdXRvcGxvdC5PcGVuU3RyZWV0TWFwKHJhc3RlcjJfdXRtLCBleHBhbmQgPSBUUlVFKSArIHRoZW1lX2J3KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemU9MSkpICsKICBnZW9tX3RpbGUoZGF0YT1iYl9pbWFnZSwgCiAgICAgICAgICAgIGFlcyh4PWJiX2ltYWdlQGNvb3Jkc1ssMV0sIHk9YmJfaW1hZ2VAY29vcmRzWywyXSwKICAgICAgICAgICAgZmlsbCA9IGJiX2ltYWdlQGRhdGEkdWQpKSArCiAgZ2VvbV9wb2x5Z29uKGRhdGE9YmJfcG9seSwgYWVzKHg9eCwgeT15LCBncm91cCA9IGdyb3VwKSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiaW5mZXJubyIpICsgYW5ub3RhdGlvbl9jdXN0b20oYmJfdW5pdHMpICsKICBsYWJzKHg9IkVhc3RpbmcgKG0pIiwgeT0iTm9ydGhpbmcgKG0pIiwgdGl0bGU9IjQyNDEiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSkpCmJiLnBsb3QKYGBgCgo8YnI+CgpgYGB7ciBCcm93bmlhbiBCcmlkZ2UgTW92ZW1lbnQgMiwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNuYWtlXzQyNzAgPC0gcmVhZC5jc3YoIklEXzQyNzAuY3N2IikKZGF0ZSA8LSBhcy5QT1NJWGN0KHN0cnB0aW1lKGFzLmNoYXJhY3RlcihzbmFrZV80MjcwJHRpbWVzdGFtcCksIiVZLSVtLSVkICVIOiVNOiVTIiwgdHo9IkFzaWEvQmFuZ2tvayIpKQpzbmFrZV80MjcwJGRhdGUgPC0gZGF0ZQpzbmFrZV80MjcwLnJlbG9jIDwtIGNiaW5kLmRhdGEuZnJhbWUoc25ha2VfNDI3MCR1dG0uZWFzdGluZywgc25ha2VfNDI3MCR1dG0ubm9ydGhpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMudmVjdG9yKHNuYWtlXzQyNzAkaW5kaXZpZHVhbC5sb2NhbC5pZGVudGlmaWVyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5QT1NJWGN0KGRhdGUpKQpjb2xuYW1lcyhzbmFrZV80MjcwLnJlbG9jKSA8LSBjKCJ4IiwieSIsImlkIiwiZGF0ZSIpCnRyYWplY3RvcnkgPC0gYXMubHRyYWooc25ha2VfNDI3MC5yZWxvYywgZGF0ZT1kYXRlLCBpZD0ic25ha2VfNDI3MCIpCnNpZzEgPC0gbGlrZXIodHJhamVjdG9yeSwgc2lnMiA9IDU4LCByYW5nZXNpZzEgPSBjKDAsIDUpLCBwbG90aXQgPSBGQUxTRSkKc25ha2VfNDI3MC50cmFqIDwtIGtlcm5lbGJiKHRyYWplY3RvcnksIHNpZzEgPSAuNzkwOCwgc2lnMiA9IDU4LCBncmlkID0gMTAwKQpiYl92ZXIgPC0gZ2V0dmVydGljZXNocihzbmFrZV80MjcwLnRyYWosIDI0LjUpCmJiX3BvbHkgPC0gZm9ydGlmeShiYl92ZXIsIHJlZ2lvbiA9ICJpZCIsIAogICAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmcgPSBDUlMoIitwcm9qPXV0bSArem9uZT0xNisKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikpCmNvbG5hbWVzKGJiX3BvbHkpIDwtIGMoIngiLCJ5Iiwib3JkZXIiLCJob2xlIiwicGllY2UiLCJpZCIsImdyb3VwIikKYmJfaW1hZ2UgPC0gY3JvcChzbmFrZV80MjcwLnRyYWosIGJiX3ZlciwgCiAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmcgPSBDUlMoIitwcm9qPXV0bSArem9uZT0xNiArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0dW09V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKSkKYmJfdW5pdHMgPC0gZ3JpZC50ZXh0KHBhc3RlKHJvdW5kKGJiX3ZlciRhcmVhLDIpLCIgaGEiKSwgeD0wLjg1LCAgeT0wLjk1LAogICAgICAgICAgICAgICAgICAgZ3A9Z3Bhcihmb250ZmFjZT00LCBjb2w9IndoaXRlIiwgY2V4PTAuOSksIGRyYXcgPSBGQUxTRSkKYmIucGxvdCA8LSBhdXRvcGxvdC5PcGVuU3RyZWV0TWFwKHJhc3RlcjJfdXRtLCBleHBhbmQgPSBUUlVFKSArIHRoZW1lX2J3KCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGw9TkEsIHNpemU9MSkpICsKICBnZW9tX3RpbGUoZGF0YT1iYl9pbWFnZSwgCiAgICAgICAgICAgIGFlcyh4PWJiX2ltYWdlQGNvb3Jkc1ssMV0sIHk9YmJfaW1hZ2VAY29vcmRzWywyXSwKICAgICAgICAgICAgZmlsbCA9IGJiX2ltYWdlQGRhdGEkdWQpKSArCiAgZ2VvbV9wb2x5Z29uKGRhdGE9YmJfcG9seSwgYWVzKHg9eCwgeT15LCBncm91cCA9IGdyb3VwKSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gTkEpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiaW5mZXJubyIpICsgYW5ub3RhdGlvbl9jdXN0b20oYmJfdW5pdHMpICsKICBsYWJzKHg9IkVhc3RpbmcgKG0pIiwgeT0iTm9ydGhpbmcgKG0pIiwgdGl0bGU9IjQyNzAiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSkpCmJiLnBsb3QKYGBgCgo8YnI+IAoKKipJbnRlcmVzdGluZ2x5LCBldmVuIHRob3VnaCA0MjcwIGhhZCBhIGhvbWUgcmFuZ2UgdGhhdCB3YXMgZmFpcmx5IGNvbXBhcmFibGUgdG8gNDI0MSBpbiBzaXplIChhdCBsZWFzdCBieSBtY3ApLCBhZGRpbmcgdGhlIHRpbWVzdGFtcCBkYXRhIGFuZCBnZW5lcmF0aW5nIHRoaXMgJ2hlYXQgbWFwJyBvZiBhY3Rpdml0eSBzaG93cyB0aGF0IDQyNzAncyBhY3Rpdml0eSB3YXMgZ2VuZXJhbGx5IGNvbmNlbnRyYXRlZCBpbiBhIG11Y2ggZGVuc2VyIGFyZWEuKiog